package com.swan.test.listener;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.IThrowableProxy;
import ch.qos.logback.classic.spi.ThrowableProxy;
import ch.qos.logback.core.UnsynchronizedAppenderBase;

import java.io.Closeable;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.*;

/** 自定义日志处理器
 * @author zongf
 * @date 2024-02-04
 */
public class LogEventListenerAppender extends UnsynchronizedAppenderBase<ILoggingEvent> {

    private static List<ILogEventListener> logHandlers = new ArrayList<>();

    // spring 初始化后回调
    public static synchronized void setLogHandlers(List<ILogEventListener> handlers) {
        if (Objects.nonNull(handlers)) {
            logHandlers = handlers;
        }
    }

    @Override
    protected void append(ILoggingEvent eventObject) {
        if (!isStarted()) {
            return;
        }

        subAppend(eventObject);
    }

    protected void subAppend(ILoggingEvent eventObject) {
        if (!isStarted()) {
            return;
        }

        // 解析日志
        LogInfo logInfo = this.parseLogEvent(eventObject);

        // 回调监听器
        for (ILogEventListener logHandler : logHandlers) {
            logHandler.handle(logInfo);
        }

    }

    private LogInfo parseLogEvent(ILoggingEvent loggingEvent) {

        LogInfo logInfo = new LogInfo();
        logInfo.setEvent(loggingEvent);

        logInfo.setLogTime(loggingEvent.getTimeStamp());
        logInfo.setLogger(loggingEvent.getLoggerName());
        logInfo.setThreadName(loggingEvent.getThreadName());
        logInfo.setLevel(loggingEvent.getLevel().toString());
        logInfo.setMessage(loggingEvent.getFormattedMessage());
        logInfo.setThrowableStack(this.parseThrowable(loggingEvent));
        logInfo.setMdcProperty(loggingEvent.getMDCPropertyMap());

        // 日志类名和行数
        StackTraceElement[] callerArray = loggingEvent.getCallerData();
        if (callerArray != null && callerArray.length > 0) {
            StackTraceElement callerDatum = loggingEvent.getCallerData()[0];
            logInfo.setClassName(callerDatum.getClassName());
            logInfo.setMethodName(callerDatum.getMethodName());
            logInfo.setLineNumber(callerDatum.getLineNumber());
        }

        return logInfo;
    }

    private String parseThrowable(ILoggingEvent loggingEvent) {
        if (Objects.isNull(loggingEvent.getThrowableProxy())) {
            return null;
        }

        IThrowableProxy throwableProxy = loggingEvent.getThrowableProxy();
        if (!(throwableProxy instanceof ThrowableProxy)) {
            return null;
        }
        ThrowableProxy txProxy = (ThrowableProxy) loggingEvent.getThrowableProxy();
        if (Objects.isNull(txProxy)) {
            return null;
        }

        StringBuffer sb = new StringBuffer();
        StringWriter sw = null;
        PrintWriter pw = null;
        try {
            sw = new StringWriter();
            pw = new PrintWriter(sw);
            txProxy.getThrowable().printStackTrace(pw);
            sb.append(sw.toString());
        } catch (Throwable t) {

        }finally {
            close(sw, pw);
        }
        return sb.toString();
    }

    public static void close(Closeable... closeables) {
        for (Closeable closeable : closeables) {
            try {
                if(closeable != null){
                    closeable.close();
                }
            } catch (Exception e) {
               // do nothing
            }
        }
    }
}
